在 JS 世界中執行環境是根據不同 function ,做區分的不同的函示,執行環境會是不同的。
直接用個簡單例子,就可以看出這個觀念:
function FN1(){
const string = 'test'
console.log(string)
}
function FN2(){
console.log(string)
}
FN1() //test
FN2() //string is not defined
console.log(string) //string is not defined
如同上面範例中,FN2
函示中、以及全域底下是無法找到 FN1 中的 string
常數,值得一提的是 JS 最底層的執行環境,就是全域本身(window) 。
執行堆疊是和上面執行環境相關的觀念,一般狀況下,函示執行完畢,函示記憶體便會釋放,比如這個範例:
function FN1(){
console.log('FN1')
}
function FN2(){
console.log('FN2')
}
FN1()
FN2()
範例的執行順序就會是:
執行 FN1 函示 ⇒ FN1 函示執行完畢,釋放記憶體 ⇒ 執行 FN2 函示 ⇒ FN2 函示執行完畢,釋放記憶體 。
當然這是一般狀況,如果我們函示中又『呼叫』了另外的函示,那麼就會造成執行堆疊的狀況,比如這個範例:
function FN1(parmNum){
const number1 = parmNum + 1
FN2(number1)
}
function FN2(parmNum){
const number2 = parmNum + 1
FN3(number2)
}
function FN3(parmNum){
const number3 = parmNum + 1
}
FN1(1)
這邊可以使用 chrome 瀏覽器的 Sources 功能來觀察 執行堆疊 狀況:
(這邊要注意的是圖中的 Call Stack 狀況,Call Stack 就是執行堆疊狀態)
從上面 GIF 可以發現,FN1()
呼叫了 FN2()
, 這種函示呼叫另一個函示時,Call Stack 就會一層一層疊起來,而這個堆疊順序是根據 『怎麼呼叫函示』有關,而非函示的宣告順序,因此根據程式碼執行堆疊順序就會是:FN1()
⇒ FN2()
⇒ FN3()
而上面有提到函示執行完畢,會將記憶體釋放,而堆疊後的函示釋放順序則不會是一般人預期中的:FN1()
⇒ FN2()
⇒ FN3()
而是由最後被執行的函示開始釋放,因此釋放的順序會是:FN3()
⇒ FN2()
⇒ FN1()